﻿using SharpCompress.Archives;
using SharpCompress.Common;
using SharpCompress.Readers;
using SharpCompress.Writers;

namespace HHFileViewServer
{
    /// <summary>
    /// 压缩包
    /// </summary>
    public class CompressionService : IService
    {
        private readonly ILogger<CompressionService> _logger;
        public CompressionService(ILogger<CompressionService> logger)
        {
            _logger = logger;
        }

        /// <summary>
        /// 压缩包转换
        /// </summary>
        /// <param name="fileInfo">文件信息</param>
        /// <returns></returns>
        public BaseFileInfo Conversion(BaseFileInfo fileInfo, string password)
        {
            // 解压的地址
            string folder = fileInfo.Name.Replace(".", "_");
            string target = Const.AbsolutePath(folder);
            if (!Directory.Exists(target))
                Directory.CreateDirectory(target);

            string source = fileInfo.AbsolutePath;
            try
            {
                switch (fileInfo.Extension.ToLower())
                {
                    case ".rar":
                        this.RARDecompression(source, target, password);
                        break;
                    case ".zip":
                    case ".7z":
                        this.ZIPDecompression(source, target, password);
                        break;
                    case ".tar.gz":
                        this.TARDecompression(source, target);
                        break;
                    default:
                        throw new CustomException($"解压缩失败，压缩包类型({fileInfo.Extension})不支持");
                }
            }
            catch (CryptographicException)
            {
                // 需要解压密码
                throw;
            }
            catch (Exception ex)
            {
                _logger.LogError($"压缩包({source})解压失败。", ex);
            }
            fileInfo.Files = FileHelper.GetFolderFiles(folder, target);
            if (fileInfo.Files.Count < 1)
            {
                throw new CustomException($"解压缩失败。");
            }
            return fileInfo;
        }

        /// <summary>
        /// 解压缩（RAR）
        /// </summary>
        public void RARDecompression(string source, string target, string password)
        {
            using (Stream stream = File.OpenRead(source))
            {
                IReader reader = null;
                if (string.IsNullOrEmpty(password))
                    reader = ReaderFactory.Open(stream);
                else
                    reader = ReaderFactory.Open(stream, new ReaderOptions() { Password = password });
                while (reader.MoveToNextEntry())
                {
                    if (!reader.Entry.IsDirectory)
                    {
                        reader.WriteEntryToDirectory(
                            target,
                            new ExtractionOptions()
                            {
                                ExtractFullPath = true,
                                Overwrite = true,
                            });
                    }
                }
            }
        }

        /// <summary>
        /// 解压缩（ZIP，7z）
        /// </summary>
        public void ZIPDecompression(string source, string target, string password)
        {
            IArchive archive = null;
            if (string.IsNullOrEmpty(password))
                archive = ArchiveFactory.Open(source);
            else
                archive = ArchiveFactory.Open(source, new ReaderOptions() { Password = password });

            foreach (var entry in archive.Entries)
            {
                if (!entry.IsDirectory)
                {
                    entry.WriteToDirectory(
                        target,
                        new ExtractionOptions()
                        {
                            ExtractFullPath = true,
                            Overwrite = true
                        });
                }
            }
            archive.Dispose();
        }

        /// <summary>
        /// 解压缩（.tar.gz）
        /// </summary>
        public void TARDecompression(string source, string target)
        {
            using (var tar = File.OpenWrite(source))
            {
                using (var zipWriter = WriterFactory.Open(tar, ArchiveType.Tar, CompressionType.GZip))
                {
                    zipWriter.WriteAll(
                        target,
                        "*",
                        SearchOption.AllDirectories);
                }
            }
        }
    }
}
